package com.googlecode.continuity.core.server.security;

import java.util.Date;
import java.util.Set;

import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.GrantedAuthorityImpl;
import org.acegisecurity.userdetails.UserDetails;
import com.googlecode.continuity.core.client.ContinuityUser;

/**
 * To seperate security concerns from normal user account data, the SecurityUserToken exists as a second user handling
 * object.  It is coupled to the Acegi security framework and wraps its corresponding
 * {@link com.googlecode.continuity.core.client.ContinuityUser} object which doesn't know of its existence.
 *
 * @author Peter Schmitt <aragos@aragos.de>
 */
public class SecurityUserToken implements UserDetails {
// ------------------------------ FIELDS ------------------------------

  private int id;
  private String password;
  private Date expiresOn;
  private Date lockedFrom;
  private Date lockedThrough;
  private Date credentialsExpireOn;
  private boolean enabled;

  private ContinuityUser user;

// --------------------- GETTER / SETTER METHODS ---------------------

  /**
   * Returns the date and time at which this user's credentials expire. Can be null if no such time is set
   *
   * @return date and time at which this user's credentials expire (null if no such time is set)
   */
  public Date getCredentialsExpireOn() {
    return credentialsExpireOn;
  }

  /**
   * Set a time at which the user's credentials expire. The user usually gets locked out of the system once this given
   * time has passed until the date is reset. Can be null if no such date is desired.
   *
   * @param credentialsExpireOn new time and date at which this user's credentials expire (can be null for no expiration)
   */
  public void setCredentialsExpireOn(Date credentialsExpireOn) {
    this.credentialsExpireOn = credentialsExpireOn;
  }

  /**
   * Returns the expiration date of this user account. Returns null if no expiration is set.
   *
   * @return expiration date for this user (can be null for no expiration)
   */
  public Date getExpiresOn() {
    return expiresOn;
  }

  /**
   * Sets a new expiration date and time for this user.  Can be null if no expiration is planned.
   *
   * @param expiresOn new expiration date for this user (can be null for no expiration)
   */
  public void setExpiresOn(Date expiresOn) {
    this.expiresOn = expiresOn;
  }

  /**
   * Returns this user's unique id. This id is identical with the one stored in the corresponding
   * {@link com.googlecode.continuity.core.client.ContinuityUser}.
   *
   * @return user's unique id
   */
  public int getId() {
    return id;
  }

  /**
   * Returns the date and time at which this user's lock starts.  A locked user cannot log into the system between the
   * lock start and end date.
   *
   * @return lock start date and time
   */
  public Date getLockedFrom() {
    return lockedFrom;
  }

  /**
   * Set a new date and time at which this user's lock starts.  A locked user cannot log into the system between the
   * lock start and end date.
   *
   * @param lockedFrom new lock start date and time
   */
  public void setLockedFrom(Date lockedFrom) {
    this.lockedFrom = lockedFrom;
  }

  /**
   * Returns the date and time at which this user's lock ends.  A locked user cannot log into the system between the
   * lock start and end date.
   *
   * @return lock end date and time
   */
  public Date getLockedThrough() {
    return lockedThrough;
  }

  /**
   * Set a new date and time at which this user's lock ends.  A locked user cannot log into the system between the
   * lock start and end date.
   *
   * @param lockedThrough new lock end date and time
   */
  public void setLockedThrough(Date lockedThrough) {
    this.lockedThrough = lockedThrough;
  }

  /**
   * Returns this user's password.
   *
   * @return this user's password
   */
  public String getPassword() {
    return password;
  }

  /**
   * Sets a new password for this user.
   *
   * @param password new password
   */
  public void setPassword(String password) {
    this.password = password;
  }

  /**
   * Returns the wrapped {@link com.googlecode.continuity.core.client.ContinuityUser} instance.
   *
   * @return corresponding {@link com.googlecode.continuity.core.client.ContinuityUser} instance
   */
  public ContinuityUser getUser() {
    return user;
  }

  /**
   * Set a new corresponding user for this security token.
   *
   * @param user new corresponding user
   */
  public void setUser(ContinuityUser user) {
    this.user = user;
  }

  /**
   * Returns true if this user is enabled.  Not enabled users cannot log into the system.
   *
   * @return true if this user is enabled, false otherwise
   */
  public boolean isEnabled() {
    return enabled;
  }

  /**
   * Sets the enabled status for this user.  Not enabled users cannot log into the system.
   *
   * @param enabled new enabled status
   */
  public void setEnabled(boolean enabled) {
    this.enabled = enabled;
  }

// ------------------------ INTERFACE METHODS ------------------------

// --------------------- Interface UserDetails ---------------------

  /**
   * Returns the authorities granted to this user.
   *
   * @return authorities granted to this user
   */
  public GrantedAuthority[] getAuthorities() {
    Set<com.googlecode.continuity.core.client.GrantedAuthority> authorities = user.getAuthorities();
    GrantedAuthority[] ret = new GrantedAuthority[authorities.size()];
    int i = 0;
    for (com.googlecode.continuity.core.client.GrantedAuthority auth : authorities) {
      ret[i++] = new GrantedAuthorityImpl("ROLE_" + auth.getName().toUpperCase());
    }
    return ret;
  }

  /**
   * Returns this user's name.
   *
   * @return this user's name
   */
  public String getUsername() {
    return user.getUsername();
  }

  /**
   * Checks whether this user account is not expired yet by using the expiration date.
   *
   * @return true if user is not expired, false otherwise
   * @see #getExpiresOn()
   */
  public boolean isAccountNonExpired() {
    Date now = new Date(System.currentTimeMillis());
    return expiresOn == null || now.before(expiresOn);
  }

  /**
   * Checks whether this user account is currently locked by utilizing the lock dates.
   *
   * @return true if user is not locked, false otherwise
   * @see #getLockedFrom()
   * @see #getLockedThrough()
   */
  public boolean isAccountNonLocked() {
    Date now = new Date(System.currentTimeMillis());
    return lockedFrom == null || lockedThrough == null || now.after(lockedThrough) || now.before(lockedFrom);
  }

  /**
   * Checks whether this user account's credentials are expired by utilizing the credentials expiration date.
   *
   * @return true if credentials are not expired, false otherwise
   * @see #getCredentialsExpireOn()
   */
  public boolean isCredentialsNonExpired() {
    Date now = new Date(System.currentTimeMillis());
    return credentialsExpireOn == null || now.before(credentialsExpireOn);
  }
}
